#include "Client.h"
#include <cassert>
#include <iostream>

#include "Channel.h"
#include "Impl.h"
#include "NetFrameImplFactory.h"

namespace frame {


class ConnectHandler : public ConnectHandlerImpl{
public:
    ConnectHandler()
    {
    }
    ~ConnectHandler()override {}

    void connected(ChannelImpl::Ptr channelImpl) override{
        std::cout << "ConnectHandler::connected" << std::endl;
        auto chnl = getChannel();
        if(chnl != nullptr){
            chnl->setImpl(channelImpl);

            auto clt = getClient();
            if(clt != nullptr){
                clt->add(chnl);
                chnl->open();

                chnl->setConnected(true);
                chnl->getHandlerAdapter()->connected(chnl);
            }
            else{
                std::cout << "ERROR: client must be set first" << std::endl;
            }

        }
        else{
            std::cout << "ERROR: channel must be set first" << std::endl;
        }
    }
    void connectFailed(ChannelImpl::Ptr channelImp, int error) override{
        auto chnl = getChannel();
        if(chnl != nullptr) {
            auto handler = chnl->getHandlerAdapter();
            if(handler != nullptr)
                handler->connectFailed(chnl, error);
        }
    }

    IClient::Ptr getClient() const {
        return client.lock();
    }
    void setClient(IClient::Ptr client){
        this->client = client;
    }

    IChannel::Ptr getChannel() const {
        return channel;
    }
    void setChannel(IChannel::Ptr channel){
        this->channel = channel;
    }

private:
    std::weak_ptr<IClient> client;
    IChannel::Ptr channel;
};


class ChannelHandler : public ChannelHandlerImpl {
public:
    ChannelHandler(){}
    ~ChannelHandler()override{}

    void disconnected(ChannelImpl::Ptr channelImpl, int error) override{
        std::cout << "ChannelHandler::disconnected" << std::endl;
        auto chnl = getChannel();
        if(chnl != nullptr){
            chnl->setConnected(false);
            chnl->close();
            auto clnt = getClient();
            if(clnt != nullptr)
                clnt->remove(chnl->getId());

            auto hdlr = chnl->getHandlerAdapter();
            if(hdlr != nullptr){
                hdlr->disconnected(chnl, error);
            }
        }
        else{
            std::cout << "ERROR:channel is nullptr" << std::endl;
        }
    }

    void read(ChannelImpl::Ptr channelImpl, const char * buf,  std::size_t size) override{
        std::cout << "ChannelHandler::read" << std::endl;
        auto chnl = getChannel();
        if(chnl != nullptr){
            auto hdlr = chnl->getHandlerAdapter();
            if(hdlr != nullptr)
                hdlr->read(chnl, buf, size);
        }
    }
    void readException(ChannelImpl::Ptr channelImpl, int error) override{
        std::cout << "ChannelHandler::readException" << std::endl;
        auto chnl = getChannel();
        if(chnl != nullptr){
            auto hdlr = chnl->getHandlerAdapter();
            if(hdlr != nullptr)
                hdlr->readException(chnl, error);
        }
    }

    void writed(ChannelImpl::Ptr channelImpl, std::size_t size) override{
        std::cout << "ChannelHandler::writed" << std::endl;
        auto chnl = getChannel();
        if(chnl != nullptr){
            auto hdlr = chnl->getHandlerAdapter();
            if(hdlr != nullptr)
                hdlr->writed(chnl, size);
        }
    }
    void writeException(ChannelImpl::Ptr channelImpl, int error) override {
        std::cout << "ChannelHandler::writeException" << std::endl;
        auto chnl = getChannel();
        if(chnl != nullptr){
            auto hdlr = chnl->getHandlerAdapter();
            if(hdlr != nullptr)
                hdlr->writeException(chnl, error);
        }
    }

public:
    IClient::Ptr getClient() const {
        return client.lock();
    }
    void setClient(IClient::Ptr client){
        this->client = client;
    }
    IChannel::Ptr getChannel() const {
        return channel;
    }
    void setChannel(IChannel::Ptr channel){
        this->channel = channel;
    }
private:
    std::weak_ptr<IClient> client;
    IChannel::Ptr channel;
};


Client::Client(IService::Ptr service, IChannelHandlerAdapter::Ptr defaultChannelHandlerAdapter)
: factory(service->getFactory())
, service(service)
, id(0)
, defaultChannelHandlerAdapter(defaultChannelHandlerAdapter)
, opened(false)
, connectHandler(std::make_shared<ConnectHandler>())
, channelHandler(std::make_shared<ChannelHandler>())
{
    std::cout << "Client:constructor" << std::endl;
}

Client::~Client() {
    std::cout << "Client::destructor" << std::endl;
}

int Client::open() {
    std::cout << "Client::open" << std::endl;;

    impl = factory->createClient(getService()->getImpl(), connectHandler); // clientImpl <--> connectHandler
    connectHandler->setClient(shared_from_this()); // ConnectHandler <--> client
    channelHandler->setClient(shared_from_this()); 

    assert(impl != nullptr);
    if(!opened){
        int ret= impl->open();
        opened = true;
        return ret;
    }
    return 0;
};

void Client::close() {
    std::cout << "Client::close, open:" << opened << std::endl;
    assert(impl != nullptr);
    if(opened){
        impl->close();

        for(auto & p : channelMap){
            p.second->close();
        }
        opened = false;
    }
}
IChannel::Ptr Client::connect(const std::string & remoteIp, const int remotePort, IChannelHandlerAdapter::Ptr channelHandlerAdapter) {
    std::cout <<  "Client::connect" << std::endl;
    assert(impl != nullptr);

    //1. create Channel
    channelHandlerAdapter = channelHandlerAdapter == nullptr ? getDefaultChannelHandlerAdapter() : channelHandlerAdapter;
    IChannel::Ptr channel = std::make_shared<Channel>(channelHandlerAdapter);
    if(channel == nullptr){
        return nullptr;
    }
    //2. connect channel to connectHandler & channelHandler
    connectHandler->setChannel(channel);
    channelHandler->setChannel(channel); 

    //3. create channelImpl with clientImpl->connect()
    ChannelImpl::Ptr channelImpl = impl->connect(remoteIp, remotePort, channelHandler);// impl --> async connect/read/write
    if(channelImpl == nullptr){
        std::cout << "ERROR:No Memory for create ChannelImpl" << std::endl;
        return nullptr;
    }
    return channel;
}

uint64_t Client::add(IChannel::Ptr channel){
    uint64_t id = numbers.next();
    channel->setId(id);
    channelMap.insert(std::make_pair(id, channel));
    return id;
}
void Client::remove(uint64_t id){
    auto iter = channelMap.find(id);
    if(iter != channelMap.end()){
        channelMap.erase(iter);
    }
}
size_t Client::size(){
    return channelMap.size();
}
IChannel::Ptr Client::get(uint64_t id){
    auto iter = channelMap.find(id);
    if(iter != channelMap.end()){
        return iter->second;
    }
    return nullptr;
}


int Client::broadcast(const char * buffer, size_t len){
    for(auto & p : channelMap){
        p.second->write(buffer, len);
    }
    return 0;
}

} //end of namespace frame
